package com.funo.logic.generate;

import cn.hutool.core.io.FileUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.mapper.Mapper;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.funo.logic.constant.ClassEnum;
import com.funo.logic.constant.CodeConstants;
import com.funo.logic.constant.Modifier;
import com.funo.logic.constant.NodeTypeConstant;
import com.funo.logic.generate.code.*;
import com.funo.logic.generate.code.logic.LogicIfCodeSupport;
import com.funo.logic.generate.extand.VariableArgument;
import com.funo.logic.generate.file.CodeFile;
import com.funo.logic.generate.wrapper.AnnotationClassWrapper;
import com.funo.logic.generate.wrapper.ClassWrapper;
import com.funo.logic.generate.wrapper.GenericsClassWrapper;
import com.funo.logic.generate.wrapper.MethodInvokeWrapper;
import com.funo.logic.model.*;
import com.funo.logic.model.database.Table;
import com.funo.logic.model.logicflow.FlowEdge;
import com.funo.logic.model.logicflow.FlowNode;
import com.funo.logic.response.HandleResult;
import com.funo.logic.service.*;
import com.funo.logic.util.GenerateBaseUtil;
import com.funo.logic.util.HumpUnderLineUtil;
import com.google.common.collect.Lists;
import lombok.Data;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.*;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 生成代码服务
 * @since 2024年7月23日11:02:13
 * @author cms
 */
@Service
public class GenerateCodeService {

    private static final Log LOG = LogFactory.get(GenerateCodeService.class);

    @Autowired
    private LogicArrangeService logicArrangeService;
    @Autowired
    private TableService tableService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RequestParameterService requestParameterService;
    @Autowired
    private QueryConfigService queryConfigService;
    @Autowired
    private LogicConfigService logicConfigService;
    @Autowired
    private SingleOperateConfigService singleOperateConfigService;

    public List<CodeFile> generateJavaCode(LogicService logicService) {
        String pathStart = "D:\\gitpull\\ddf";
        String value = logicArrangeService.getById(logicService.getLogicArrangeId());
        if (StringUtils.isBlank(value)) {
            return null;
        }
        ClassCodeSupport controllerSupport = generateControllerCode(Lists.newArrayList(logicService));
        String conPath = pathStart + "controller";
        if (!FileUtil.exist(conPath)) {
            FileUtil.mkdir(conPath);
        }
        File controllerFile = FileUtil.touch(conPath + "\\"+  GenerateBaseUtil.getControllerName(logicService.getModuleClass()) + ".java");
        FileUtil.appendUtf8String(controllerSupport.generateCode(), controllerFile);


        ClassCodeSupport serviceSupport = generateServiceCode(logicService);
        String servicePath = pathStart + "service";
        if (!FileUtil.exist(servicePath)) {
            FileUtil.mkdir(servicePath);
        }
        File serviceFile = FileUtil.touch(servicePath + "\\"+  GenerateBaseUtil.getServiceName(logicService.getModuleClass()) + ".java");
        FileUtil.appendUtf8String(serviceSupport.generateCode(), serviceFile);


        ClassCodeSupport serviceImplSupport = generateServiceImplCode(logicService);
        String serviceImplPath = pathStart + "service\\impl";
        if (!FileUtil.exist(serviceImplPath)) {
            FileUtil.mkdir(serviceImplPath);
        }
        File serviceImplFile = FileUtil.touch(serviceImplPath + "\\"+  GenerateBaseUtil.getServiceImplName(logicService.getModuleClass()) + ".java");
        FileUtil.appendUtf8String(serviceImplSupport.generateCode(), serviceImplFile);

        ClassCodeSupport entitySupport = generateEntityCode(logicService);
        String entityPath = pathStart + "entity";
        if (!FileUtil.exist(entityPath)) {
            FileUtil.mkdir(entityPath);
        }
        File entityFile = FileUtil.touch(entityPath + "\\"+  logicService.getModuleClass() + ".java");
        FileUtil.appendUtf8String(entitySupport.generateCode(), entityFile);

        ClassCodeSupport mapperSupport = generateMapperCode(logicService);
        String mapperPath = pathStart + "dao";
        if (!FileUtil.exist(mapperPath)) {
            FileUtil.mkdir(mapperPath);
        }
        File mapperFile = FileUtil.touch(mapperPath + "\\"+  GenerateBaseUtil.getMapperName(logicService.getModuleClass()) + ".java");
        FileUtil.appendUtf8String(mapperSupport.generateCode(), mapperFile);
        return null;
    }

    /**
     * 生成实体类代码
     * @since 2024年7月23日15:29:10
     * @param logicService
     * @author cms
     */
    private ClassCodeSupport generateEntityCode(LogicService logicService) {
        ClassCodeSupport classCodeSupport = new ClassCodeSupport();
        classCodeSupport.addModifier(Modifier.PUBLIC);
        classCodeSupport.setName(logicService.getModuleClass());
        classCodeSupport.setPackageName(CodeConstants.PACKAGE_ENTITY);
        Table table = tableService.getByModule(logicService.getModuleClass());
        // 为实体类添加注解
        classCodeSupport.addAnnotation(AnnotationClassWrapper.of(Data.class, null));
        classCodeSupport.addAnnotation(AnnotationClassWrapper.of(TableName.class, table.getTableName()));
        // 为实体类添加字段
        table.getColumnList().forEach(column -> {
            FieldCodeSupport fieldCodeSupport = new FieldCodeSupport();
            String comment = StringUtils.isNotEmpty(column.getComment()) ? column.getComment() : column.getName();
            fieldCodeSupport.addComment(comment);
            fieldCodeSupport.addAnnotation(AnnotationClassWrapper.of(TableField.class, column.getCode()));
            fieldCodeSupport.addModifier(Modifier.PRIVATE);
            fieldCodeSupport.setType(ClassWrapper.of(String.class));
            fieldCodeSupport.setName(HumpUnderLineUtil.underlineToCamel(column.getCode()));
            classCodeSupport.addField(fieldCodeSupport);
        });
//        LOG.info(classCodeSupport.generateCode());
        return classCodeSupport;
    }

    /**
     * 创建生成controller代码
     * @param logicServiceList 这边的逻辑服务配置需要做数据过滤，所有的moduleClass要一致，
     *                   意味着属于同一个moduleClass
     * @since 2024年7月24日10:46:14
     * @author cms
     */
    private ClassCodeSupport generateControllerCode(List<LogicService> logicServiceList) {
        String moduleClass = logicServiceList.get(0).getModuleClass();
        ClassCodeSupport classCodeSupport = new ClassCodeSupport();
        classCodeSupport.addModifier(Modifier.PUBLIC);
        classCodeSupport.setName(moduleClass + "Controller");
        classCodeSupport.setPackageName(CodeConstants.PACKAGE_CONTROLLER);
        // 引入操作实体类
        this.importModuleClass(classCodeSupport, moduleClass, true);
        // 添加注解
        classCodeSupport.addAnnotation(AnnotationClassWrapper.of(RestController.class, null));
        classCodeSupport.addAnnotation(AnnotationClassWrapper.of(RequestMapping.class, HumpUnderLineUtil.lowerCaseLetters(moduleClass)));

        // 引用当前模块对应的service
        FieldCodeSupport fieldCodeSupport = new FieldCodeSupport();
        fieldCodeSupport.addAnnotation(AnnotationClassWrapper.of(Autowired.class, null));
        classCodeSupport.addImportedType(ClassWrapper.of(Autowired.class));
        fieldCodeSupport.addModifier(Modifier.PRIVATE);
        fieldCodeSupport.setType(ClassWrapper.of(GenerateBaseUtil.getServiceName(moduleClass)));
        fieldCodeSupport.setName(HumpUnderLineUtil.lowerCaseLetters(GenerateBaseUtil.getServiceName(moduleClass)));
        classCodeSupport.addField(fieldCodeSupport);

        // 循环添加controller方法
        for (LogicService logicService: logicServiceList) {
            MethodCodeSupport methodCodeSupport = new MethodCodeSupport();
            // 设置方法名
            methodCodeSupport.setName(logicService.getCode());
            if (logicService.getRequestMode().equals("POST")) {
                methodCodeSupport.addAnnotation(AnnotationClassWrapper.of(PostMapping.class, logicService.getCode()));
            }
            if (logicService.getRequestMode().equals("GET")) {
                methodCodeSupport.addAnnotation(AnnotationClassWrapper.of(GetMapping.class, logicService.getCode()));
            }
            String comment = StringUtils.isNotEmpty(logicService.getComment()) ? logicService.getComment() : logicService.getName();
            methodCodeSupport.addComment(comment);
            methodCodeSupport.addModifier(Modifier.PUBLIC);
            // TODO 获取当前服务编排对应的流程节点配置数据，并且根据设置的请求参数构建ParamCodeSupport
            String logicArrangeStr = logicArrangeService.getById(logicService.getLogicArrangeId());
            if (StringUtils.isNotBlank(logicArrangeStr)) {
                JSONObject logicArrangeJson = new JSONObject(logicArrangeStr);
                List<FlowNode> flowNodeList = logicArrangeJson.getBeanList("nodes", FlowNode.class);
                List<FlowEdge> flowEdgeList = logicArrangeJson.getBeanList("edges", FlowEdge.class);
                // 暂时写死，如果是post请求，用模块对象来接收参数
                if (logicService.getRequestMode().equals("POST")) {
                    ParamCodeSupport paramCodeSupport = ParamCodeSupport.of(ClassWrapper.of(moduleClass), moduleClass);
                    paramCodeSupport.addAnnotation(AnnotationClassWrapper.of(RequestBody.class, null));
                    methodCodeSupport.addGlobalAnnotation(AnnotationClassWrapper.of(RequestBody.class, null));
                    classCodeSupport.addImportedType(ClassWrapper.of(RequestBody.class));
                    methodCodeSupport.addParameter(paramCodeSupport);
                } else {
                    // 拿第一个节点
                    FlowNode firstNode = flowNodeList.get(0);
                    List<RequestParameter> requestParameterList = requestParameterService.getByBelongNodeId(firstNode.getId());
                    this.constructGetParam(methodCodeSupport, requestParameterList);
                }

                this.createMethodBody(methodCodeSupport, flowNodeList, flowEdgeList);
            }


            methodCodeSupport.setReturnType(ClassWrapper.of(HandleResult.class));

            classCodeSupport.addMethod(methodCodeSupport);
        }
//        LOG.info(classCodeSupport.generateCode());
        return classCodeSupport;
    }

    /**
     * 生成service代码
     * @param logicService
     * @since 2024年7月25日11:09:17
     * @author cms
     */
    private ClassCodeSupport generateServiceCode(LogicService logicService) {
        String moduleClass = logicService.getModuleClass();
        ClassCodeSupport classCodeSupport = new ClassCodeSupport();
        classCodeSupport.addModifier(Modifier.PUBLIC);
        classCodeSupport.setName(GenerateBaseUtil.getServiceName(moduleClass));
        classCodeSupport.setPackageName(CodeConstants.PACKAGE_SERVICE);
        classCodeSupport.setClassEnum(ClassEnum.INTERFACE);
        // 引入操作实体类
        this.importModuleClass(classCodeSupport, moduleClass, false);
        // 添加接口实现类
        GenericsClassWrapper genericsClassWrapper = GenericsClassWrapper.of(IService.class);
        genericsClassWrapper.addGenerics(ClassWrapper.of(moduleClass));
        classCodeSupport.addSuperInterfaces(genericsClassWrapper);

//        LOG.info(classCodeSupport.generateCode());
        return classCodeSupport;
    }

    /**
     * 生成实现类代码
     * @param logicService
     * @since 2024年7月25日11:10:02
     * @author cms
     */
    private ClassCodeSupport generateServiceImplCode(LogicService logicService) {
        String moduleClass = logicService.getModuleClass();
        ClassCodeSupport classCodeSupport = new ClassCodeSupport();
        classCodeSupport.addModifier(Modifier.PUBLIC);
        classCodeSupport.setName(GenerateBaseUtil.getServiceImplName(moduleClass));
        classCodeSupport.setPackageName(CodeConstants.PACKAGE_SERVICE_IMPL);
        classCodeSupport.setClassEnum(ClassEnum.CLASS);
        // 引入操作实体类
        this.importModuleClass(classCodeSupport, moduleClass, true);
        // 添加注解
        classCodeSupport.addAnnotation(AnnotationClassWrapper.of(Service.class, null));
        // 添加继承类
        GenericsClassWrapper genericsClassWrapper = GenericsClassWrapper.of(ServiceImpl.class);
        genericsClassWrapper.addGenerics(ClassWrapper.of(GenerateBaseUtil.getMapperName(moduleClass)));
        genericsClassWrapper.addGenerics(ClassWrapper.of(moduleClass));
        classCodeSupport.setSuperClass(genericsClassWrapper);

        // 添加实现类
        classCodeSupport.addSuperInterfaces(GenericsClassWrapper.of(GenerateBaseUtil.getServiceName(moduleClass), CodeConstants.PACKAGE_SERVICE));
        classCodeSupport.addImportedType(GenericsClassWrapper.of(GenerateBaseUtil.getMapperName(moduleClass), CodeConstants.PACKAGE_MAPPER));
//        LOG.info(classCodeSupport.generateCode());
        return classCodeSupport;
    }

    /**
     * 生成mapper代码
     * @param logicService
     * @since 2024年7月25日11:43:16
     * @author cms
     */
    public ClassCodeSupport generateMapperCode(LogicService logicService) {
        String moduleClass = logicService.getModuleClass();
        ClassCodeSupport classCodeSupport = new ClassCodeSupport();
        classCodeSupport.addModifier(Modifier.PUBLIC);
        classCodeSupport.setName(GenerateBaseUtil.getMapperName(moduleClass));
        classCodeSupport.setPackageName(CodeConstants.PACKAGE_MAPPER);
        classCodeSupport.setClassEnum(ClassEnum.INTERFACE);
        // 引入操作实体类
        this.importModuleClass(classCodeSupport, moduleClass, false);
        classCodeSupport.addAnnotation(AnnotationClassWrapper.of(Mapper.class, null));
        // 添加接口实现类
        GenericsClassWrapper genericsClassWrapper = GenericsClassWrapper.of(BaseMapper.class);
        genericsClassWrapper.addGenerics(ClassWrapper.of(moduleClass));
        classCodeSupport.addSuperInterfaces(genericsClassWrapper);

//        LOG.info(classCodeSupport.generateCode());
        return classCodeSupport;
    }

    /**
     * 引入当前模块的相关java文件
     * @param classCodeSupport
     * @param moduleClass
     * @param includeService 是否包含引入service
     * @since 2024年7月25日09:12:37
     */
    private void importModuleClass(ClassCodeSupport classCodeSupport, String moduleClass, boolean includeService) {
        // 引入实体对象
        classCodeSupport.addImportedType(ClassWrapper.of(moduleClass, CodeConstants.PACKAGE_ENTITY));
        // 引入service
        if (includeService) {
            classCodeSupport.addImportedType(ClassWrapper.of(GenerateBaseUtil.getServiceName(moduleClass), CodeConstants.PACKAGE_SERVICE));
        }
    }

    /**
     * 为请求的方法体构造请求参数
     * @param methodCodeSupport
     * @param requestParameters
     * @since 2024年7月25日14:52:47
     * @author cms
     */
    private void constructGetParam(MethodCodeSupport methodCodeSupport, List<RequestParameter> requestParameters) {
        requestParameters.stream().forEach(parameter -> {
            ParamCodeSupport paramCodeSupport = ParamCodeSupport.of(ClassWrapper.of(String.class), parameter.getCode());
            methodCodeSupport.addParameter(paramCodeSupport);
        });
    }

    /**
     * 根据逻辑编排节点
     * 创建方法体内部代码逻辑
     * @param methodCodeSupport
     * @param flowNodes
     * @param flowEdges
     * @since 2024年7月25日15:16:05
     * @author cms
     */
    private void createMethodBody(MethodCodeSupport methodCodeSupport, List<FlowNode> flowNodes, List<FlowEdge> flowEdges) {
        List<VariableArgument> variableArguments = new ArrayList<>();
        // 存放需要被排除的节点
        List<FlowNode> excludeNodeList = new ArrayList<>();
        for (FlowNode flowNode: flowNodes) {
            // 判断当前节点在被排除的节点集合内，继续循环
            if (excludeNodeList.stream().anyMatch(node -> node.getId().equals(flowNode.getId()))) {
                continue;
            }
            List<FlowEdge> sourceEdges = flowEdges.stream().filter(edge -> edge.getSourceNodeId().equals(flowNode.getId())).collect(Collectors.toList());
            for (FlowEdge flowEdge: sourceEdges) {
                String targetNodeId = flowEdge.getTargetNodeId();
                FlowNode targetNode = flowNodes.stream().filter(node -> node.getId().equals(targetNodeId)).findFirst().get();
                // 单条数据查询
                if (targetNode.getProperties().getType().equals(NodeTypeConstant.DATA_TYPE.GET_ONE)) {
                    this.createQueryMethodBody(methodCodeSupport, targetNode, variableArguments);
                }
                if (targetNode.getProperties().getType().equals("logicIf")) {
                    this.createIfMethodBody(methodCodeSupport, targetNode, variableArguments, flowNodes, flowEdges, excludeNodeList);
                }
            }
        }
    }

    /**
     * 查询节点代码构建
     * @param methodCodeSupport
     * @param flowNode
     * @param variableArgumentList
     * @since 2024年7月25日20:38:34
     */
    private void createQueryMethodBody(MethodCodeSupport methodCodeSupport, FlowNode flowNode, List<VariableArgument> variableArgumentList) {
        QueryConfig queryConfig = queryConfigService.getByBelongNodeId(flowNode.getId());
        if (queryConfig == null) {
            return;
        }
        MethodFieldCodeSupport methodFieldCodeSupport = new MethodFieldCodeSupport();
        methodFieldCodeSupport.setReturnType(ClassWrapper.of(queryConfig.getQueryResponse().getModule()));
        String variableName = "db" + queryConfig.getQueryResponse().getModule();
        methodFieldCodeSupport.setVariableName(variableName);
        variableArgumentList.add(new VariableArgument(variableName, flowNode.getId(), ClassWrapper.of(queryConfig.getQueryResponse().getModule())));
        // 方法命名
        String methodName = "";
        if(queryConfig.getQueryParameterList().size() == 1) {
            methodName = "getBy" + HumpUnderLineUtil.upperCaseLetters(queryConfig.getQueryParameterList().get(0).getCode());
        } else {
            String paramStr = queryConfig.getQueryParameterList().stream().map(parameter -> HumpUnderLineUtil.upperCaseLetters(parameter.getCode())).collect(Collectors.joining());
            methodName = "getBy" + paramStr;
        }

        MethodInvokeWrapper methodInvokeWrapper = MethodInvokeWrapper.of(GenerateBaseUtil.getServiceName(queryConfig.getQueryResponse().getModule()), methodName);
        methodFieldCodeSupport.setServiceWrapper(methodInvokeWrapper);
        // 参数
        for (ConditionParameter conditionParameter: queryConfig.getQueryParameterList()) {
            // 用对象接收参数
            if (methodCodeSupport.getGlobalAnnotationList().stream().anyMatch(annotation -> annotation.getFullName().equals(RequestBody.class.getName()))) {
                ParamCodeSupport paramCodeSupport = ParamCodeSupport.of(ClassWrapper.of(String.class), HumpUnderLineUtil.lowerCaseLetters(queryConfig.getModule()) + ".get" + HumpUnderLineUtil.upperCaseLetters(conditionParameter.getCode()) + "()");
                methodFieldCodeSupport.addParamCodeSupport(paramCodeSupport);
            } else {
                ParamCodeSupport paramCodeSupport = ParamCodeSupport.of(ClassWrapper.of(String.class), conditionParameter.getCode());
                methodFieldCodeSupport.addParamCodeSupport(paramCodeSupport);
            }
        }
        methodCodeSupport.addCodeSupport(methodFieldCodeSupport);
    }

    /**
     * 创建逻辑if节点代码
     * @param methodCodeSupport
     * @param flowNode
     * @param variableArgumentList
     * @since 2024年7月25日20:39:29
     * @author cms
     */
    private void createIfMethodBody(MethodCodeSupport methodCodeSupport, FlowNode currentNode, List<VariableArgument> variableArgumentList
            , List<FlowNode> flowNodeList, List<FlowEdge> flowEdgeList, List<FlowNode> excludeNodeList) {
        LogicConfig logicConfig = logicConfigService.getByBelongNodeId(currentNode.getId());
        if (logicConfig == null) {
            return;
        }
        LogicIfCodeSupport logicIfCodeSupport = new LogicIfCodeSupport();
        logicIfCodeSupport.setLogicType(NodeTypeConstant.LOGIC_TYPE.IF);
        logicIfCodeSupport.addLogicItemList(logicConfig.getItems());
        // TODO 暂时就取第一个配置逻辑判断的参数
        VariableArgument variableArgument = variableArgumentList.stream().filter(argument -> argument.getBelongNodeId().equals(logicConfig.getItems().get(0).getVariable())).findFirst().get();
        logicIfCodeSupport.setVariableArgument(variableArgument);
        // TODO 多个逻辑内部代码
        List<FlowEdge> sourceEdges = flowEdgeList.stream().filter(edge -> edge.getSourceNodeId().equals(currentNode.getId())).collect(Collectors.toList());
        // demo暂时只做一个目标节点
        FlowEdge edge = sourceEdges.get(0);
        FlowNode targetNode = flowNodeList.stream().filter(node -> node.getId().equals(edge.getTargetNodeId())).findFirst().get();
        excludeNodeList.add(targetNode);
        SingleOperateConfig singleOperateConfig = singleOperateConfigService.getByBelongNodeId(targetNode.getId());
        if (singleOperateConfig != null) {
            MethodFieldCodeSupport methodFieldCodeSupport = new MethodFieldCodeSupport();
            String methodName = singleOperateConfig.getOperateType().equals("add") ? "save" : "updateById";
            methodFieldCodeSupport.setServiceWrapper(MethodInvokeWrapper.of(GenerateBaseUtil.getServiceName(singleOperateConfig.getModule()), methodName));
            methodFieldCodeSupport.addParamCodeSupport(ParamCodeSupport.of(ClassWrapper.of(singleOperateConfig.getModule()),  HumpUnderLineUtil.lowerCaseLetters(singleOperateConfig.getModule())));
            logicIfCodeSupport.setMethodFieldCodeSupport(methodFieldCodeSupport);
            methodCodeSupport.addCodeSupport(logicIfCodeSupport);
        }

    }
}
